Skip to content

fix: use custom modulemap to prevent Xcode 26 ODR errors with CocoaPods#428

Open
mfazekas wants to merge 4 commits intorive-app:mainfrom
mfazekas:mfazekas/fix-cocoapods-xcode26-odr
Open

fix: use custom modulemap to prevent Xcode 26 ODR errors with CocoaPods#428
mfazekas wants to merge 4 commits intorive-app:mainfrom
mfazekas:mfazekas/fix-cocoapods-xcode26-odr

Conversation

@mfazekas
Copy link
Contributor

@mfazekas mfazekas commented Mar 6, 2026

Summary

When the XCFramework is built, the Swift build phase unconditionally appends a `module RiveRuntime.Swift` block to every `module.modulemap` slice, referencing `RiveRuntime-Swift.h`. This header bakes in Swift stdlib C++ interop type definitions (e.g. parameter names in `swift::Optional::init`) for the exact Swift/Xcode version used to build the XCFramework.

When consumers use a different Xcode/Swift version alongside a package that enables Swift/C++ interop (e.g. react-native-nitro-modules), both modules end up with conflicting definitions of the same C++ types in the same compilation unit, causing hard ODR errors:

```
error: 'swift::Optional' has different definitions in different modules;
definition in module 'RiveRuntime.Swift' found 1st parameter named 'some_' ← built with Xcode 16.4
but in 'NitroModules.Swift' found 1st parameter named 'value' ← built with Xcode 26
```

Why MODULEMAP_FILE alone doesn't work

Setting `MODULEMAP_FILE` in xcconfig provides a custom base modulemap, but Xcode's Swift build phase always appends the `module RiveRuntime.Swift { ... }` block afterward, regardless of what the custom modulemap contains. There is no build setting to prevent this.

Fix

Post-process every `module.modulemap` in the assembled XCFramework after `xcodebuild -create-xcframework` to strip the `module RiveRuntime.Swift { ... }` block. Without this entry, Clang never loads the stale `RiveRuntime-Swift.h` into the module graph, so the conflicting C++ type definitions are never visible to consumers.

This makes the shipped XCFramework compatible with any Xcode/Swift version, for both CocoaPods and SPM consumers (SPM downloads the already-patched artifact, so no consumer-side hooks are needed).

Options considered

Option Xcode 16 Xcode 26 CocoaPods SPM
Rebuild with Xcode 26
Post-process XCFramework at release time (this PR)
CocoaPods post_install hook (consumer-side workaround)
Source SPM distribution (long-term)

Testing

Verified with an Expo/React Native app using `@rive-app/react-native` + `react-native-nitro-modules` on Xcode 26: ODR errors are gone after the `post_install` hook applies the same strip. Zero `swift::Optional` / `swift::UTF8View` / lambda ODR errors in the build output.

See rive-app/rive-nitro-react-native#174

mfazekas and others added 4 commits March 6, 2026 07:53
The auto-generated modulemap includes a .Swift Clang submodule referencing
RiveRuntime-Swift.h, which bakes in Swift stdlib C++ interop definitions for
the exact Swift version used to build. When CocoaPods consumers use a different
Xcode/Swift version, the differing type definitions cause hard ODR errors.
…bmodule

The Swift build phase unconditionally appends a `module RiveRuntime.Swift`
block to the framework's module.modulemap, referencing RiveRuntime-Swift.h.
This header bakes in Swift stdlib C++ interop type definitions (e.g.
swift::Optional::init parameter names) for the exact Swift version used to
build the XCFramework. When consumers use a different Xcode/Swift version,
these definitions conflict with the consumer's own Swift module definitions,
causing hard ODR errors that break the build.

The MODULEMAP_FILE build setting cannot prevent this: Xcode appends the
Swift submodule unconditionally after applying any custom modulemap.

Fix: post-process every module.modulemap in the assembled XCFramework to
strip the `module RiveRuntime.Swift { ... }` block. Without this entry,
Clang never loads the stale RiveRuntime-Swift.h into the module graph, so
the conflicting C++ type definitions are never seen.

This makes the shipped XCFramework compatible with any Xcode/Swift version
for both CocoaPods and SPM consumers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The custom modulemap file is no longer needed. The Xcode 26 ODR fix
is handled entirely by the post-process step in build_framework.sh
which strips the `module RiveRuntime.Swift` block from the assembled
XCFramework after build.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant